#ifndef __M_BUF_H__
#define __M_BUF_H__
/* 
    单个缓冲区的进一步设计：
    设计一个缓冲区：直接存放格式化后的日志消息字符串
    好处：
    1.减少了LogMsg对象频繁的构造的消耗
    2.可以针对缓冲区中的日志消息，一次性进行IO操作，减少IO次数，提高效率
        缓冲区类的设计：
        1.管理一个存放字符串数据的缓冲区（使用vecotor进行空间管理）
        2.当前的写入数据位置的指针（指向可写区域的起始位置，避免数据的写入覆盖）
        3.当前的读取数据位置的指针（指向可读数据区域的起始位置，
        当读取指针与写入指针指向相同位置表示数据取完了）
            提供的操作：
            1.向缓冲区中写入数据
            2.获取可读数据起始地址的接口
            3.获取可读数据长度的接口
            4.移动读写位置的接口
            5.初始化缓冲区的操作（将读写位置初始化--将一个缓冲区所有数据处理完毕之后）
    
    因为是双缓冲区，写缓冲区每次都是从头开始写，读缓冲区每次都是从头开始读，直到尾部。
    且每次交换缓冲区会把缓冲区格式化，所以不存在环形队列的问题
*/
#include "util.hpp"
#include <vector>
#include <cassert>

namespace log {
    #define DEFAULT_BUFFER_SIZE (10 * 1024 * 1024) // 10M 初始缓冲区大小
    #define THRESHOLD_BUFFER_SIZE (80 * 1024 * 1024) // 80M 阈值
    #define INCREMENT_BUFFER_SIZE (10 * 1024 * 1024) // 10M 增量-线性
    class Buffer {
    public:
        Buffer() :writer_idx_(0), reader_idx_(0), buffer_(DEFAULT_BUFFER_SIZE) {}
        // 写入数据
        void push(const char *data, size_t len) {
            /* buffer不再判断是否扩容，而是只要有数据就写入，不够直接就扩容。把
                把是否扩容的选择交给使用buffer的外界，外界可以根据buffer public
                的writeable_size接口，在禁止扩容时进行判断，可写大小不够时外部进行处理即可。
            */
            // 1.判断扩容 分为 [允许扩容] 和 [不允许扩容]
            // 1.1 不允许扩容，len大于可写入数据长度时直接返回，写入失败
            // if (len > writeable_size()) return;
            // 1.2 允许扩容，设定阈值，小于阈值时2被扩容，大于等于阈值时线性（固定值）扩容
            // 测试时可以允许扩容，用于测试上限；实际不允许扩容，防止循环写入导致一直扩容，使机器崩溃
            reserve_enough_size(len);
            // 2.copy
            std::copy(data, data + len, &buffer_[writer_idx_]);
            // 3.移动写指针
            move_writer(len);
        }
        // 获取可读数据的起始地址
        const char* begin() {
            return &buffer_[reader_idx_];
        }
        // 获取可读数据的长度 - 即还剩多少没读的数据
        size_t readable_size() const {
            return writer_idx_ - reader_idx_;
        }
        // 获取可写数据的长度 - 即还剩多少没写入的空间
        // 针对不允许扩容版本，因为允许扩容版本一直是可写的
        size_t writeable_size() {
            return buffer_.size() - writer_idx_;
        }
        // 移动读写指针len个长度
        void move_reader(size_t len) {
            assert(len <= readable_size());
            reader_idx_ += len;
        }
        // 重置缓冲区指针，格式化缓冲区
        void reset() {
            writer_idx_ =  reader_idx_ = 0;
        }
        // 交换缓冲区指针
        void swap(Buffer &buffer) {
            buffer_.swap(buffer.buffer_);
            std::swap(writer_idx_, buffer.writer_idx_);
            std::swap(reader_idx_, buffer.reader_idx_);
        }
        // 判断是否为空
        bool empty() const { return writer_idx_ == reader_idx_; }
    private:
        void move_writer(size_t len) {
            assert(writer_idx_ + len <= buffer_.size());
            writer_idx_ += len;
        }
        // 扩容，设定阈值，小于阈值时2被扩容，大于等于阈值时线性（固定值）扩容
        void reserve_enough_size(size_t len) {
            if (len <= writeable_size()) return;
            int new_size = 0;
            if (buffer_.size() < THRESHOLD_BUFFER_SIZE) {
                new_size = buffer_.size() * 2 + len;
            }
            else {
                new_size = buffer_.size() + INCREMENT_BUFFER_SIZE + len;
            }
            buffer_.resize(new_size);
        }
    private:
        std::vector<char> buffer_;
        size_t writer_idx_;
        size_t reader_idx_;
    };
}

#endif